package sketch.seg.cd;

import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import sketch.ink.SampledPoint;
import sketch.ink.Stroke;

public class CDStaticKH extends CornerDetector {

	private int K1 = 3, K2 = 9;

	private double H1 = 0.6, H2 = 2;

	private boolean singleStep = false;

	public void setSingleStep(boolean b) {
		clear();
		singleStep = b;
	}

	public boolean isSingleStep() {
		return singleStep;
	}

//	private static CDStaticKH instance = null;

	public CDStaticKH() {

	}

	public CDStaticKH(Stroke s) {
		super(s);
	}

	private double[] rhos = null;

	public double[] getRhos() {
		if (rhos == null)
			detectCorners();
		return rhos;
	}

	public void setRhos(double[] rhos) {
		this.rhos = rhos;
	}

	public int getK1() {
		return K1;
	}

	public void setK1(int k1) {
		clear();
		K1 = k1;
	}

	public int getK2() {
		return K2;
	}

	public void setK2(int k2) {
		clear();
		K2 = k2;
	}

	public double getH1() {
		return H1;
	}

	public void setH1(double h1) {
		clear();
		H1 = h1;
	}

	public double getH2() {
		return H2;
	}

	public void setH2(double h2) {
		clear();
		H2 = h2;
	}

	private List<SampledPoint> cd(List<SampledPoint> points, int K, double H) {
		List<SampledPoint> corners = new ArrayList<SampledPoint>();
		double[] rhos = new double[points.size()];

		if (points == null)
			return corners;

		SampledPoint[] ps = new SampledPoint[points.size()];
        int i1 = 0;
        for (Iterator<SampledPoint> it = points.iterator(); it.hasNext(); ) {
        	ps[i1++] = it.next();
        }

        for (int i = 0; i < ps.length; i++) {
            rhos[i] = relativeRho(ps, i, K);
        }
        setRhos(rhos);

        int maxIndex = 0;
        double maxRho = -1;
        int state = 0;
        for (int i = 0; i < rhos.length; i++) {
            if (rhos[i] > H && state == 0) {
                state = 1;
                maxRho = rhos[i];
                maxIndex = i;
            } else if (rhos[i] > H && state == 1) {
                if (rhos[i] > maxRho) {
                    maxRho = rhos[i];
                    maxIndex = i;
                }
            } else if (rhos[i] < H && state == 1) {
                state = 0;
                corners.add(ps[maxIndex]);
                maxRho = -1;
                maxIndex = 0;
            }
        }
        if (state == 1)
            corners.add(ps[maxIndex]);

		return corners;
	}

	protected void detectCorners() {
		super.detectCorners();

//        rhos = new double[getPoints().size()];
        List<SampledPoint> tmp = cd(getPoints(), getK1(), getH1());

        if (isSingleStep())
        	setCorners(tmp);
        else {
        	List<SampledPoint> corners = new ArrayList<SampledPoint>();

        	Iterator<SampledPoint> itSeg = tmp.iterator();
        	Iterator<SampledPoint> itAll = getPoints().iterator();

        	List<SampledPoint> ps;// = new ArrayList<SampledPoint>();

        	while (itSeg.hasNext()) {
       			SampledPoint end = itSeg.next();

       			ps = new ArrayList<SampledPoint>();
       			while (itAll.hasNext()) {
       				SampledPoint cur = itAll.next();
       				ps.add(cur);
       				if (cur.equals(end))
       					break;
       			}

       			corners.addAll(cd(ps, getK2(), getH2()));
       			corners.add(end);
        	}

        	ps = new ArrayList<SampledPoint>();
        	while (itAll.hasNext()) {
        		ps.add(itAll.next());
        	}
        	corners.addAll(cd(ps, getK2(), getH2()));
//        	SampledPoint cmp = null;
//        	List<SampledPoint> ps = new ArrayList<SampledPoint>();
//        	for (; itAll.hasNext() && it.hasNext(); ) {
//        		if (cmp == null)
//        			cmp = it.next();
//        		SampledPoint point = itAll.next();
//
//        		ps.add(point);
//        		if (cmp.equals(point)) {
//        			corners.addAll(cd(ps, getK2(), getH2()));
//        			corners.add(cmp);
//        			ps = new ArrayList<SampledPoint>();
//        			ps.add(cmp);
//        			cmp = null;
//        		}
//        	}
//        	for (; itAll.hasNext(); ) {
//        		ps.add(itAll.next());
//        	}
//        	corners.addAll(cd(ps, getK2(), getH2()));

        	setCorners(corners);
        }

	}

	public static double relativeRho(SampledPoint[] ps, int I, int K) {
        if (I < K || I >= ps.length - K)
            return 0;

        Line2D line = new Line2D.Double(ps[I-K].getX(), ps[I-K].getY(),
                ps[I+K].getX(), ps[I+K].getY());

        double dis = 0;
        double len = line.getP1().distance(line.getP2());
        for (int i = I - K + 1; i < I + K; i++) {
            dis += Math.abs(line.ptLineDist(ps[i].toPoint2D()));
        }
//		System.out.println(dis + ":" + len);
        return Math.abs(dis/len);
    }
}
